﻿/*
* Copyright (C) 2020 ~ 2024 chaigec All Rights Reserved.
*
* Author:     chaigec <chaigec@163.com>
* Maintainer: chaigec <chaigec@163.com>

 * Use of this source code is governed by MIT license that can be found in the
 * LICENSE file in the root of the source tree. All contributing project authors
 * may be found in the AUTHORS file in the root of the source tree.
*/

#include "MkTime.h"
#include "MkLog.h"

#define NtpTimeOffset    ((70LL * 365 + 17) * 24 * 60 * 60)

#ifdef _WIN32
int gettimeofday(struct timeval *tp, void *tzp)
{
    time_t clock;
    struct tm tm;
    SYSTEMTIME wtm;
    GetLocalTime(&wtm);
    tm.tm_year = wtm.wYear - 1900;
    tm.tm_mon = wtm.wMonth - 1;
    tm.tm_mday = wtm.wDay;
    tm.tm_hour = wtm.wHour;
    tm.tm_min = wtm.wMinute;
    tm.tm_sec = wtm.wSecond;
    tm.tm_isdst = -1;
    clock = mktime(&tm);
    tp->tv_sec = static_cast<long>(clock);
    tp->tv_usec = wtm.wMilliseconds * 1000;
    return (0);
}
#else
#include <sys/time.h>
#endif

CMkTime::CMkTime()
    : m_TvSec(0)
    , m_TvUsec(0)
    , m_Year(0)
    , m_Month(0)
    , m_Day(0)
    , m_Hour(0)
    , m_Minute(0)
    , m_Second(0)
    , m_WeekDay(0)
{

}

CMkTime::CMkTime(Uint64 TvSec, Uint64 TvUsec)
    : m_TvSec(TvSec)
    , m_TvUsec(TvUsec)
    , m_Year(0)
    , m_Month(0)
    , m_Day(0)
    , m_Hour(0)
    , m_Minute(0)
    , m_Second(0)
    , m_WeekDay(0)
{
    ParseTvSec();
}

CMkTime::CMkTime(const MkString& strTime, const MkString& strSpace/*="T"*/, const MkString& strEnd/*=""*/, BOOL WithMillSec/* = FALSE*/)
    : m_TvSec(0)
    , m_TvUsec(0)
    , m_Year(0)
    , m_Month(0)
    , m_Day(0)
    , m_Hour(0)
    , m_Minute(0)
    , m_Second(0)
    , m_WeekDay(0)
{
    ParseStringTime(strTime, strSpace, strEnd, WithMillSec);
}

CMkTime::CMkTime(Uint32 Year, Uint32 Month, Uint32 Day, Uint32 Hour/* = 0*/, Uint32 Minite/* = 0*/, Uint32 Second/* = 0*/, Uint64 USecond/* = 0*/)
    : m_TvSec(0)
    , m_TvUsec(USecond)
    , m_Year(Year)
    , m_Month(Month)
    , m_Day(Day)
    , m_Hour(Hour)
    , m_Minute(Minite)
    , m_Second(Second)
{
    struct tm tm;
    tm.tm_year = m_Year - 1900;
    tm.tm_mon = m_Month - 1;
    tm.tm_mday = 0 == m_Day ? 1 : m_Day;
    tm.tm_hour = m_Hour;
    tm.tm_min = m_Minute;
    tm.tm_sec = m_Second;
    tm.tm_isdst = -1;
    m_TvSec = mktime(&tm);
    ParseTvSec();
}

Uint32 CMkTime::GetLogString(MkString& strTime) const
{
    char pbuf[Len512] = { 0 };
    snprintf(pbuf, Len512, "%04d-%02d-%02d %02d:%02d:%02d.%03d", m_Year, m_Month, m_Day, m_Hour, m_Minute, m_Second, static_cast<int>(m_TvUsec / 1000));
    strTime = pbuf;
    return NoneError;
}

Uint32 CMkTime::GetString(MkString& strTime, const MkString& strSpace/*="T"*/, const MkString& strEnd/*=""*/, BOOL WithMillSec/* = FALSE*/) const
{
    char pbufDay[Len32] = { 0 };
    char pbufSec[Len32] = { 0 };
    char pbufMillSec[Len32] = { 0 };
    snprintf(pbufDay, Len32, "%04d-%02d-%02d", m_Year, m_Month, m_Day);
    snprintf(pbufSec, Len32, "%02d:%02d:%02d", m_Hour, m_Minute, m_Second);
    snprintf(pbufMillSec, Len32, ".%03d", static_cast<int>(m_TvUsec / 1000));
    strTime = MkString(pbufDay) + strSpace + MkString(pbufSec) + strEnd;
    if (WithMillSec) {
        strTime += pbufMillSec;
    }
    return NoneError;
}

Uint32 CMkTime::GetNumberString(MkString &strTime, const MkString& strSpace/* = "t"*/, const MkString& strEnd/* = ""*/, BOOL WithMillSec/* = FALSE*/) const
{
    char pbufDay[Len32] = { 0 };
    char pbufSec[Len32] = { 0 };
    char pbufMillSec[Len32] = { 0 };
    snprintf(pbufDay, Len32, "%04d%02d%02d", m_Year, m_Month, m_Day);
    snprintf(pbufSec, Len32, "%02d%02d%02d", m_Hour, m_Minute, m_Second);
    snprintf(pbufMillSec, Len32, "%03d", static_cast<int>(m_TvUsec / 1000));
    strTime = MkString(pbufDay) + strSpace + MkString(pbufSec) + strEnd;
    if (WithMillSec) {
        strTime += pbufMillSec;
    }
    return NoneError;
}

Uint32 CMkTime::GetUnderLineString(MkString &strTime, const MkString& strSpace/* = "T"*/, const MkString& strEnd/* = ""*/, BOOL WithMillSec/* = FALSE*/) const
{
    char pbufDay[Len32] = { 0 };
    char pbufSec[Len32] = { 0 };
    char pbufMillSec[Len32] = { 0 };
    snprintf(pbufDay, Len32, "%04d_%02d_%02d", m_Year, m_Month, m_Day);
    snprintf(pbufSec, Len32, "%02d_%02d_%02d", m_Hour, m_Minute, m_Second);
    snprintf(pbufMillSec, Len32, "%03d", static_cast<int>(m_TvUsec / 1000));
    strTime = MkString(pbufDay) + strSpace + MkString(pbufSec) + strEnd;
    if (WithMillSec) {
        strTime += pbufMillSec;
    }
    return NoneError;
}

Uint32 CMkTime::GetDayString(MkString& strTime) const
{
    char pbuf[Len512] = { 0 };
    snprintf(pbuf, Len512, "%04d_%02d_%02d", m_Year, m_Month, m_Day);
    strTime = pbuf;
    return NoneError;
}

Uint32 CMkTime::GetFileString(MkString& strTime) const
{
    char pbuf[Len512] = { 0 };
    snprintf(pbuf, Len512, "%04d_%02d_%02d_%02d_%02d_%02d_%03llu", m_Year, m_Month, m_Day, m_Hour, m_Minute, m_Second, m_TvUsec / 1000);
    strTime = pbuf;
    return NoneError;
}

Uint32 CMkTime::GetGMTString(MkString& strTime) const
{
    char szTemp[Len512] = { 0 };
    struct tm *pTm = localtime((time_t*)&m_TvSec);
    strftime(szTemp, sizeof(szTemp), "%a, %d %b %Y %H:%M:%S GMT", pTm);
    strTime = szTemp;
    return NoneError;
}

Uint32 CMkTime::ParseTvSec()
{
    struct tm *pTm = localtime((time_t*)&m_TvSec);
    if (!pTm) {
        return UnknownError;
    }
    m_Year = pTm->tm_year + 1900;
    m_Month = pTm->tm_mon + 1;
    m_Day = pTm->tm_mday;
    m_Hour = pTm->tm_hour;
    m_Minute = pTm->tm_min;
    m_Second = pTm->tm_sec;
    m_WeekDay = pTm->tm_wday;
    return NoneError;
}

//2020-06-23T10:44:04.923
Uint32 CMkTime::ParseStringTime(const MkString& strTime, const MkString& strSpace/*="T"*/, const MkString& strEnd/*=""*/, BOOL WithMillSec/* = FALSE*/)
{
    if (strTime.empty()) {
        return NoneError;
    }

    if ("now" == strTime) {
        struct timeval tp;
        gettimeofday(&tp, nullptr);
        m_TvUsec = tp.tv_usec;
        m_TvSec = tp.tv_sec;
        ParseTvSec();
        return NoneError;
    }

    MkString strFormat = "%04d-%02d-%02d" + strSpace + "%02d:%02d:%02d" + strEnd;
    if (WithMillSec) {
        strFormat += ".%03d";
        sscanf(strTime.c_str(), strFormat.c_str(), &m_Year, &m_Month, &m_Day, &m_Hour, &m_Minute, &m_Second, &m_TvUsec);
        m_TvUsec *= 1000;
    } else {
        sscanf(strTime.c_str(), strFormat.c_str(), &m_Year, &m_Month, &m_Day, &m_Hour, &m_Minute, &m_Second);
    }

    struct tm Tm_time = { 0 };
    Tm_time.tm_year = m_Year - 1900;
    Tm_time.tm_mon = m_Month - 1;
    Tm_time.tm_mday = m_Day;
    Tm_time.tm_hour = m_Hour;
    Tm_time.tm_min = m_Minute;
    Tm_time.tm_sec = m_Second;
    m_TvSec = mktime(&Tm_time);
    ParseTvSec();
    return NoneError;
}

Uint64 CMkTime::GetNtpTime()const
{
    //1bit = 10~12 ps / 32bit
    Uint64 t = (Uint64)(m_TvUsec) << 32;
    t = t / 1000000;
    t |= (NtpTimeOffset + m_TvSec) << 32;
    return t;
}

BOOL CMkTime::IsEmpty() const
{
    return 0 == m_TvSec && 0 == m_TvUsec;
}

BOOL CMkTime::operator>(const CMkTime& Other)const
{
    if (this->m_TvSec == Other.GetTvSec()) {
        return this->m_TvUsec > Other.GetUSecond();
    }
    return m_TvSec > Other.GetTvSec();
}

BOOL CMkTime::operator>=(const CMkTime& Other)const
{
    if (this->m_TvSec == Other.GetTvSec()) {
        return this->m_TvUsec >= Other.GetUSecond();
    }
    return m_TvSec > Other.GetTvSec();
}

BOOL CMkTime::operator==(const CMkTime& Other)const
{
    return m_TvSec == Other.GetTvSec() && this->m_TvUsec == Other.GetUSecond();
}

BOOL CMkTime::operator<(const CMkTime& Other)const
{
    if (this->m_TvSec == Other.GetTvSec()) {
        return this->m_TvUsec < Other.GetUSecond();
    }
    return m_TvSec < Other.GetTvSec();
}

BOOL CMkTime::operator<=(const CMkTime& Other)const
{
    if (this->m_TvSec == Other.GetTvSec()) {
        return this->m_TvUsec <= Other.GetUSecond();
    }
    return m_TvSec < Other.GetTvSec();
}

Uint64 CMkTime::operator-(const CMkTime& Other)const
{
    if (this->m_TvSec < Other.GetTvSec()) {
        return 0;
    }
    return (this->m_TvSec - Other.GetTvSec()) * 1000 * 1000 + this->m_TvUsec - Other.GetUSecond();
}

CMkTime CMkTime::operator-(Uint64 uSec)const
{
    Uint64 nRet = this->m_TvSec * 1000 * 1000 + m_TvUsec - uSec;
    return CMkTime(nRet / (1000 * 1000), nRet % (1000 * 1000));
}

CMkTime CMkTime::operator-=(Uint64 uSec)
{
    Uint64 nRet = this->m_TvSec * 1000 * 1000 + m_TvUsec - uSec;
    this->m_TvSec = nRet / (1000 * 1000);
    this->m_TvUsec = nRet % (1000 * 1000);
    ParseTvSec();
    return *this;
}

CMkTime CMkTime::operator+(Uint64 uSec)const
{
    Uint64 nRet = this->m_TvSec * 1000 * 1000 + m_TvUsec + uSec;
    return CMkTime(nRet / (1000 * 1000), nRet % (1000 * 1000));
}

CMkTime CMkTime::operator+=(Uint64 uSec)
{
    Uint64 nRet = this->m_TvSec * 1000 * 1000 + m_TvUsec + uSec;
    this->m_TvSec = nRet / (1000 * 1000);
    this->m_TvUsec = nRet % (1000 * 1000);
    ParseTvSec();
    return *this;
}

void CMkTime::operator=(const CMkTime& Other)
{
    this->m_TvSec = Other.GetTvSec();
    this->m_TvUsec = Other.GetUSecond();
    this->m_Year = Other.GetYear();
    this->m_Month = Other.GetMonth();
    this->m_Day = Other.GetDay();
    this->m_Hour = Other.GetHour();
    this->m_Minute = Other.GetMinute();
    this->m_Second = Other.GetSecond();
    this->m_WeekDay = Other.GetWeekDay();
}

int CMkTime::OffsetHour(const CMkTime& Other)const
{
    int OffsetSeconds = 0;
    if (this->m_TvSec > Other.GetTvSec()) {
        OffsetSeconds = static_cast<int>(this->m_TvSec - Other.GetTvSec());
    } else {
        OffsetSeconds = 0 - static_cast<int>(Other.GetTvSec() - m_TvSec);
    }
    return OffsetSeconds / 3600;
}

int CMkTime::OffsetSec(const CMkTime& Other)const
{
    int OffsetSeconds = 0;
    if (this->m_TvSec > Other.GetTvSec()) {
        OffsetSeconds = static_cast<int>(this->m_TvSec - Other.GetTvSec());
    } else {
        OffsetSeconds = 0 - static_cast<int>(Other.GetTvSec() - m_TvSec);
    }
    return OffsetSeconds;
}

CMkTime::~CMkTime()
{

}
